package com.bsj.pay.util.platform.wsgw.sm;

import java.io.*;

public class SM4
{
    public static final int[] CK;
    public static final int[] FK;
    public static final int SM4_DECRYPT = 0;
    public static final int SM4_ENCRYPT = 1;
    public static final byte[] SboxTable;
    
    static {
        SboxTable = new byte[] { -42, -112, -23, -2, -52, -31, 61, -73, 22, -74, 20, -62, 40, -5, 44, 5, 43, 103, -102, 118, 42, -66, 4, -61, -86, 68, 19, 38, 73, -122, 6, -103, -100, 66, 80, -12, -111, -17, -104, 122, 51, 84, 11, 67, -19, -49, -84, 98, -28, -77, 28, -87, -55, 8, -24, -107, -128, -33, -108, -6, 117, -113, 63, -90, 71, 7, -89, -4, -13, 115, 23, -70, -125, 89, 60, 25, -26, -123, 79, -88, 104, 107, -127, -78, 113, 100, -38, -117, -8, -21, 15, 75, 112, 86, -99, 53, 30, 36, 14, 94, 99, 88, -47, -94, 37, 34, 124, 59, 1, 33, 120, -121, -44, 0, 70, 87, -97, -45, 39, 82, 76, 54, 2, -25, -96, -60, -56, -98, -22, -65, -118, -46, 64, -57, 56, -75, -93, -9, -14, -50, -7, 97, 21, -95, -32, -82, 93, -92, -101, 52, 26, 85, -83, -109, 50, 48, -11, -116, -79, -29, 29, -10, -30, 46, -126, 102, -54, 96, -64, 41, 35, -85, 13, 83, 78, 111, -43, -37, 55, 69, -34, -3, -114, 47, 3, -1, 106, 114, 109, 108, 91, 81, -115, 27, -81, -110, -69, -35, -68, 127, 17, -39, 92, 65, 31, 16, 90, -40, 10, -63, 49, -120, -91, -51, 123, -67, 45, 116, -48, 18, -72, -27, -76, -80, -119, 105, -105, 74, 12, -106, 119, 126, 101, -71, -15, 9, -59, 110, -58, -124, 24, -16, 125, -20, 58, -36, 77, 32, 121, -18, 95, 62, -41, -53, 57, 72 };
        FK = new int[] { -1548633402, 1453994832, 1736282519, -1301273892 };
        CK = new int[] { 462357, 472066609, 943670861, 1415275113, 1886879365, -1936483679, -1464879427, -993275175, -521670923, -66909679, 404694573, 876298825, 1347903077, 1819507329, -2003855715, -1532251463, -1060647211, -589042959, -117504499, 337322537, 808926789, 1280531041, 1752135293, -2071227751, -1599623499, -1128019247, -656414995, -184876535, 269950501, 741554753, 1213159005, 1684763257 };
    }
    
    private long GET_ULONG_BE(final byte[] array, final int n) {
        return (long)(array[n] & 0xFF) << 24 | (long)((array[n + 1] & 0xFF) << 16) | (long)((array[n + 2] & 0xFF) << 8) | ((long)(array[n + 3] & 0xFF) & 0xFFFFFFFFL);
    }
    
    private void PUT_ULONG_BE(final long n, final byte[] array, final int n2) {
        array[n2] = (byte)(n >> 24 & 0xFFL);
        array[n2 + 1] = (byte)(n >> 16 & 0xFFL);
        array[n2 + 2] = (byte)(n >> 8 & 0xFFL);
        array[n2 + 3] = (byte)(0xFFL & n);
    }
    
    private long ROTL(final long n, final int n2) {
        return this.SHL(n, n2) | n >> 32 - n2;
    }
    
    private long SHL(final long n, final int n2) {
        return (-1L & n) << n2;
    }
    
    private void SWAP(final long[] array, final int n) {
        final long n2 = array[n];
        array[n] = array[31 - n];
        array[31 - n] = n2;
    }
    
    private byte[] padding(final byte[] array, int n) {
        Object o = null;
        if (array != null) {
            final byte[] array2 = null;
            if (n == 1) {
                final int n2 = 16 - array.length % 16;
                final byte[] array3 = new byte[array.length + n2];
                System.arraycopy(array, 0, array3, 0, array.length);
                n = 0;
                while (true) {
                    o = array3;
                    if (n >= n2) {
                        break;
                    }
                    array3[array.length + n] = (byte)n2;
                    ++n;
                }
            }
            else {
                n = array[array.length - 1];
                o = new byte[array.length - n];
                System.arraycopy(array, 0, o, 0, array.length - n);
            }
        }
        return (byte[])o;
    }
    
    private long sm4CalciRK(long get_ULONG_BE) {
        final byte[] array = new byte[4];
        this.PUT_ULONG_BE(get_ULONG_BE, array, 0);
        get_ULONG_BE = this.GET_ULONG_BE(new byte[] { this.sm4Sbox(array[0]), this.sm4Sbox(array[1]), this.sm4Sbox(array[2]), this.sm4Sbox(array[3]) }, 0);
        return this.ROTL(get_ULONG_BE, 13) ^ get_ULONG_BE ^ this.ROTL(get_ULONG_BE, 23);
    }
    
    private long sm4F(final long n, final long n2, final long n3, final long n4, final long n5) {
        return this.sm4Lt(n2 ^ n3 ^ n4 ^ n5) ^ n;
    }
    
    private long sm4Lt(long get_ULONG_BE) {
        final byte[] array = new byte[4];
        this.PUT_ULONG_BE(get_ULONG_BE, array, 0);
        get_ULONG_BE = this.GET_ULONG_BE(new byte[] { this.sm4Sbox(array[0]), this.sm4Sbox(array[1]), this.sm4Sbox(array[2]), this.sm4Sbox(array[3]) }, 0);
        return this.ROTL(get_ULONG_BE, 2) ^ get_ULONG_BE ^ this.ROTL(get_ULONG_BE, 10) ^ this.ROTL(get_ULONG_BE, 18) ^ this.ROTL(get_ULONG_BE, 24);
    }
    
    private byte sm4Sbox(final byte b) {
        return SM4.SboxTable[b & 0xFF];
    }
    
    private void sm4_one_round(final long[] array, final byte[] array2, final byte[] array3) {
        int i = 0;
        final long[] array4 = new long[36];
        array4[0] = this.GET_ULONG_BE(array2, 0);
        array4[1] = this.GET_ULONG_BE(array2, 4);
        array4[2] = this.GET_ULONG_BE(array2, 8);
        array4[3] = this.GET_ULONG_BE(array2, 12);
        while (i < 32) {
            array4[i + 4] = this.sm4F(array4[i], array4[i + 1], array4[i + 2], array4[i + 3], array[i]);
            ++i;
        }
        this.PUT_ULONG_BE(array4[35], array3, 0);
        this.PUT_ULONG_BE(array4[34], array3, 4);
        this.PUT_ULONG_BE(array4[33], array3, 8);
        this.PUT_ULONG_BE(array4[32], array3, 12);
    }
    
    private void sm4_setkey(final long[] array, final byte[] array2) {
        final long[] array3 = new long[4];
        final long[] array4 = new long[36];
        int i = 0;
        array3[0] = this.GET_ULONG_BE(array2, 0);
        array3[1] = this.GET_ULONG_BE(array2, 4);
        array3[2] = this.GET_ULONG_BE(array2, 8);
        array3[3] = this.GET_ULONG_BE(array2, 12);
        array4[0] = (array3[0] ^ (long)SM4.FK[0]);
        array4[1] = (array3[1] ^ (long)SM4.FK[1]);
        array4[2] = (array3[2] ^ (long)SM4.FK[2]);
        array4[3] = (array3[3] ^ (long)SM4.FK[3]);
        while (i < 32) {
            array[i] = (array4[i + 4] = (array4[i] ^ this.sm4CalciRK(array4[i + 1] ^ array4[i + 2] ^ array4[i + 3] ^ (long)SM4.CK[i])));
            ++i;
        }
    }
    
    public byte[] sm4_crypt_cbc(final SM4_Context sm4_Context, byte[] array, byte[] array2) throws Exception {
        if (array == null || array.length != 16) {
            throw new Exception("iv error!");
        }
        if (array2 == null) {
            throw new Exception("input is null!");
        }
        byte[] padding = array2;
        if (sm4_Context.isPadding) {
            padding = array2;
            if (sm4_Context.mode == 1) {
                padding = this.padding(array2, 1);
            }
        }
        int i = padding.length;
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(padding);
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        if (sm4_Context.mode == 1) {
            while (i > 0) {
                final byte[] array3 = new byte[16];
                array2 = new byte[16];
                final byte[] array4 = new byte[16];
                byteArrayInputStream.read(array3);
                for (int j = 0; j < 16; ++j) {
                    array2[j] = (byte)(array3[j] ^ array[j]);
                }
                this.sm4_one_round(sm4_Context.sk, array2, array4);
                System.arraycopy(array4, 0, array, 0, 16);
                byteArrayOutputStream.write(array4);
                i -= 16;
            }
        }
        else {
            final byte[] array5 = new byte[16];
            while (i > 0) {
                final byte[] array6 = new byte[16];
                final byte[] array7 = new byte[16];
                array2 = new byte[16];
                byteArrayInputStream.read(array6);
                System.arraycopy(array6, 0, array5, 0, 16);
                this.sm4_one_round(sm4_Context.sk, array6, array7);
                for (int k = 0; k < 16; ++k) {
                    array2[k] = (byte)(array7[k] ^ array[k]);
                }
                System.arraycopy(array5, 0, array, 0, 16);
                byteArrayOutputStream.write(array2);
                i -= 16;
            }
        }
        array2 = (array = byteArrayOutputStream.toByteArray());
        if (sm4_Context.isPadding) {
            array = array2;
            if (sm4_Context.mode == 0) {
                array = this.padding(array2, 0);
            }
        }
        byteArrayInputStream.close();
        byteArrayOutputStream.close();
        return array;
    }
    
    public byte[] sm4_crypt_ecb(final SM4_Context sm4_Context, byte[] array) throws Exception {
        if (array == null) {
            throw new Exception("input is null!");
        }
        byte[] padding = array;
        if (sm4_Context.isPadding) {
            padding = array;
            if (sm4_Context.mode == 1) {
                padding = this.padding(array, 1);
            }
        }
        int i = padding.length;
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(padding);
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        while (i > 0) {
            array = new byte[16];
            final byte[] array2 = new byte[16];
            byteArrayInputStream.read(array);
            this.sm4_one_round(sm4_Context.sk, array, array2);
            byteArrayOutputStream.write(array2);
            i -= 16;
        }
        final byte[] array3 = array = byteArrayOutputStream.toByteArray();
        if (sm4_Context.isPadding) {
            array = array3;
            if (sm4_Context.mode == 0) {
                array = this.padding(array3, 0);
            }
        }
        byteArrayInputStream.close();
        byteArrayOutputStream.close();
        return array;
    }
    
    public void sm4_setkey_dec(final SM4_Context sm4_Context, final byte[] array) throws Exception {
        if (sm4_Context == null) {
            throw new Exception("ctx is null!");
        }
        sm4_Context.mode = 0;
        this.sm4_setkey(sm4_Context.sk, array);
        for (int i = 0; i < 16; ++i) {
            this.SWAP(sm4_Context.sk, i);
        }
    }
    
    public void sm4_setkey_enc(final SM4_Context sm4_Context, final byte[] array) throws Exception {
        if (sm4_Context == null) {
            throw new Exception("ctx is null!");
        }
        sm4_Context.mode = 1;
        this.sm4_setkey(sm4_Context.sk, array);
    }
}